package mdt.action;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.tigris.gef.base.Editor;
import org.tigris.gef.base.Globals;
import org.tigris.gef.base.Mode;
import org.tigris.gef.base.ModePlace;
import org.tigris.gef.graph.GraphFactory;
import org.tigris.gef.graph.GraphModel;
import org.tigris.gef.graph.MutableGraphModel;
import org.tigris.gef.undo.UndoableAction;
import org.tigris.gef.util.Localizer;
import org.tigris.gef.util.ResourceLoader;
import mdt.model.IMDTComment;
import mdt.model.INetworkDevice;
import mdt.model.MDTComment;
import mdt.model.NetworkDevice;

import javax.swing.*;
import java.awt.event.ActionEvent;
import java.util.Hashtable;

/**
 * Created by lambert on 2016/6/21.
 */
public class CreateNodeAction extends UndoableAction implements GraphFactory {

	private static final long serialVersionUID = 7466949364922746851L;

	// //////////////////////////////////////////////////////////////
	// constants
	public static Class DEFAULT_NODE_CLASS = org.tigris.gef.graph.presentation.NetNode.class;

	private static Log LOG = LogFactory.getLog(CreateNodeAction.class);

	// //////////////////////////////////////////////////////////////
	// instance variables

	// All instance variables are stored in the _args Hashtable
	private Hashtable args;
	protected String resource;

	// //////////////////////////////////////////////////////////////
	// constructors

	/** Construct a new Action with the given arguments for shapeNode class. */
	public CreateNodeAction(Hashtable args, String resource, String name) {
		super(name);
		this.args = args;
		this.resource = resource;

		Icon icon = ResourceLoader.lookupIconResource(name, name);
		if (icon != null) {
			putValue(Action.SMALL_ICON, icon);
		}
	}

	public CreateNodeAction(Hashtable args, String name) {
		this(args, "GefBase", name);
	}

	/**
	 * Construct a new Action with the given classes for the NetNode and its
	 * FigNode.
	 */
	public CreateNodeAction(Class nodeClass, String resource, String name) {
		this(new Hashtable(), resource, name);
		setArg("className", nodeClass);
	}

	public CreateNodeAction(Object nodeClass, String name, ImageIcon icon) {
		super(name, icon);
		setArg("className", nodeClass);
	}

	public CreateNodeAction(Class nodeClass, String name) {
		this(new Hashtable(), name);
		setArg("className", nodeClass);
	}

	/**
	 * Construct a new Action with the given classes for the NetNode and its
	 * FigNode, and set the global sticky mode boolean to the given value. This
	 * allows the user to place several nodes rapidly.
	 */
	public CreateNodeAction(Class nodeClass, boolean sticky, String resource,
							String name) {
		this(nodeClass, resource, name);
		setArg("shouldBeSticky", sticky ? Boolean.TRUE : Boolean.FALSE);
	}

	public CreateNodeAction(Class nodeClass, boolean sticky, String name) {
		this(nodeClass, name);
		setArg("shouldBeSticky", sticky ? Boolean.TRUE : Boolean.FALSE);
	}

	// //////////////////////////////////////////////////////////////
	// Action API

	/**
	 * Actually instanciate the NetNode and FigNode objects and set the global
	 * next mode to ModePlace
	 */
	public void actionPerformed(ActionEvent e) {
		super.actionPerformed(e);
		Editor ce = Globals.curEditor();
		GraphModel gm = ce.getGraphModel();
		if (!(gm instanceof MutableGraphModel)) {
			return;
		}
		setArg("graphModel", gm);

		String instructions = null;
		Object actionName = getValue(javax.swing.Action.NAME);
		if (actionName != null) {
			instructions = Localizer.localize("GefBase", "ClickToPlace") + " " + actionName.toString();
		}
		Mode placeMode = createMode(instructions);

		Object shouldBeSticky = getArg("shouldBeSticky");
		Globals.mode(placeMode, shouldBeSticky == Boolean.TRUE);
		if (LOG.isDebugEnabled()) {
			LOG.debug("Mode set to ModePlace with sticky mode "
					+ shouldBeSticky);
		}
	}

	/**
	 * To be overrideen on any specialist subclasses that want to supply their
	 * own modes.
	 *
	 * @param instructions
	 * @return
	 */
	protected Mode createMode(String instructions) {
		return new ModePlace(this, instructions);
	}

	// //////////////////////////////////////////////////////////////
	// GraphFactory implementation

	public GraphModel makeGraphModel() {
		return null;
	}

	public Object makeEdge() {
		return null;
	}

	/**
	 * Factory method for creating a new NetNode from the className argument.
	 * TODO This returns null on error. We need to define some basic exception
	 * classes.
	 */
	public Object makeNode() {
		Class nodeClass = (Class) getArg("className", DEFAULT_NODE_CLASS);
		try {
			if (nodeClass.equals(INetworkDevice.class))
				return new NetworkDevice();
			else if (nodeClass.equals(IMDTComment.class))
				return new MDTComment();
		} catch (IllegalArgumentException e) {
			System.err.println("Exception caught: " + e.getLocalizedMessage());
		}
		return null;
	}

	// //////////////////////////////////////////////////////////////
	// for testing purpose only
	public Object getActiveGraphModel() {
		return getArg("graphModel");
	}

	/** Store the given argument under the given name. */
	protected void setArg(String key, Object value) {
		if (args == null) {
			args = new Hashtable();
		}
		args.put(key, value);
	}

	/** Get the object stored as an argument under the given name. */
	protected Object getArg(String key) {
		if (args == null) {
			return null;
		} else {
			return args.get(key);
		}
	}

	/**
	 * Get an argument by name. If it's not defined then use the given default.
	 */
	protected Object getArg(String key, Object defaultValue) {
		if (args == null) {
			return defaultValue;
		}
		Object res = args.get(key);
		if (res == null) {
			return defaultValue;
		}
		return res;
	}
}
